﻿using System;
using System.Collections.Generic;
using SimpleWorkflow.Data;
using SimpleWorkflow.Patterns;

namespace SimpleWorkflow.Steps
{
    public delegate bool EvaluationDelegate();

    /// <summary>
    /// Make a boolean decision and branch accordingly.
    /// There are two uses: 
    /// 1) subclass and override Evaluate
    /// 2) create with evaluate delegate to ctor
    /// </summary>
    public class DecisionStep<T> : SimpleStep<T>, ILinearSteps where T : ISharedFlowContext
    {
        public EvaluationDelegate Evaluation;
        private Dictionary<bool, ISimpleStep> Branches { get; set; }

        [ComplexStepExtension]
        public bool? ExceptionOnMissingBranch { get; set; }

        protected DecisionStep(EvaluationDelegate evaluation)
            : this()
        {
            Evaluation = evaluation;
        }

        protected DecisionStep()
        {
            Branches = new Dictionary<bool, ISimpleStep>();
        }

        public override void Run()
        {
            var result = Evaluate();
            ISimpleStep branch;
            
            if (Branches.TryGetValue(result, out branch))
                RunProxy(branch);
            else if (ExceptionOnMissingBranch ?? false)
                throw new ApplicationException("Branch not found for result: " + result);
        }

        public void AddBranch(LinearSteps<T> branch)
        {
            switch (Branches.Count)
            {
                case 0:
                    Branches[true] = branch;
                    break;
                case 1:
                    Branches[false] = branch;
                    break;
                default:
                    throw new ArgumentException("Semantic violation: Two branches for decision step already exist.");
            }
        }

        public void AddStep(ISimpleStep step)
        {
            if (!(step is LinearSteps<T>))
                throw new Exception("Branch being added is not of LinearSteps");
            AddBranch((LinearSteps<T>)step);
        }

        public virtual bool Evaluate()
        {
            if (Evaluation == null)
                throw new Exception("Decision step ran with no evaluation delegate set or subclasses evaluate method not overridden.");
            return Evaluation();
        }
    }
}
